import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib

# 设置 matplotlib 使用非交互式后端以保存动画
matplotlib.use('Agg')

# 配置中文字体
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['SimSun', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Arial']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

def cubic_bezier(t, P0, P1, P2, P3):
    """计算参数 t 处的三次贝塞尔曲线点"""
    return (1-t)**3 * P0 + 3*(1-t)**2 * t * P1 + 3*(1-t) * t**2 * P2 + t**3 * P3

# 控制点
P0 = np.array([0, 0])
P1 = np.array([1, 2])
P2 = np.array([2, 0])
P3 = np.array([3, 1])

# 设置图形和坐标轴
fig, ax = plt.subplots(figsize=(8, 6))
ax.set_xlim(-0.5, 3.5)
ax.set_ylim(-0.5, 2.5)
ax.set_xlabel('X轴')
ax.set_ylabel('Y轴')
ax.set_title('三次贝塞尔曲线动画与插值连线')
ax.grid(True)

# 绘制控制点和连线
control_points, = ax.plot([P0[0], P1[0], P2[0], P3[0]], [P0[1], P1[1], P2[1], P3[1]], 'ro-', label='控制点')
curve, = ax.plot([], [], 'b-', label='贝塞尔曲线', linewidth=2)
interp_line1, = ax.plot([], [], 'g--', label='控制线')
interp_line2, = ax.plot([], [], 'g--')
interp_line3, = ax.plot([], [], 'g--')
interp_point1, = ax.plot([], [], 'go', label='一级插值点')
interp_point2, = ax.plot([], [], 'go')
interp_point3, = ax.plot([], [], 'go')
interp_connect1, = ax.plot([], [], 'm-', label='二级插值连线', linewidth=1.5)
interp_connect2, = ax.plot([], [], 'm-', linewidth=1.5)
tertiary_connect, = ax.plot([], [], 'm-', label='三级插值连线', linewidth=1.5)
moving_point, = ax.plot([], [], 'mo', label='移动点', markersize=10)
ax.legend()

# 曲线数据
t_values = np.linspace(0, 1, 100)
curve_points = np.array([cubic_bezier(t, P0, P1, P2, P3) for t in t_values])
curve_x, curve_y = [], []

def init():
    """初始化动画"""
    curve.set_data([], [])
    interp_line1.set_data([], [])
    interp_line2.set_data([], [])
    interp_line3.set_data([], [])
    interp_point1.set_data([], [])
    interp_point2.set_data([], [])
    interp_point3.set_data([], [])
    interp_connect1.set_data([], [])
    interp_connect2.set_data([], [])
    tertiary_connect.set_data([], [])
    moving_point.set_data([], [])
    return curve, interp_line1, interp_line2, interp_line3, interp_point1, interp_point2, interp_point3, interp_connect1, interp_connect2, tertiary_connect, moving_point

def animate(i):
    """更新第 i 帧的动画"""
    t = t_values[i]
    
    # 更新曲线
    curve_x.append(curve_points[i][0])
    curve_y.append(curve_points[i][1])
    curve.set_data(curve_x, curve_y)
    
    # 计算一级插值点
    Q0 = (1-t) * P0 + t * P1  # P0 和 P1 之间
    Q1 = (1-t) * P1 + t * P2  # P1 和 P2 之间
    Q2 = (1-t) * P2 + t * P3  # P2 和 P3 之间
    
    # 计算二级插值点
    R0 = (1-t) * Q0 + t * Q1  # Q0 和 Q1 之间
    R1 = (1-t) * Q1 + t * Q2  # Q1 和 Q2 之间
    
    # 计算曲线上的点
    B = cubic_bezier(t, P0, P1, P2, P3)
    
    # 更新控制线
    interp_line1.set_data([P0[0], P1[0]], [P0[1], P1[1]])
    interp_line2.set_data([P1[0], P2[0]], [P1[1], P2[1]])
    interp_line3.set_data([P2[0], P3[0]], [P2[1], P3[1]])
    
    # 更新一级插值点
    interp_point1.set_data([Q0[0]], [Q0[1]])
    interp_point2.set_data([Q1[0]], [Q1[1]])
    interp_point3.set_data([Q2[0]], [Q2[1]])
    
    # 更新二级插值连线
    interp_connect1.set_data([Q0[0], Q1[0]], [Q0[1], Q1[1]])
    interp_connect2.set_data([Q1[0], Q2[0]], [Q1[1], Q2[1]])
    
    # 更新三级插值连线（仅最新）
    tertiary_connect.set_data([R0[0], R1[0]], [R0[1], R1[1]])
    
    # 更新曲线上的移动点
    moving_point.set_data([B[0]], [B[1]])
    
    return curve, interp_line1, interp_line2, interp_line3, interp_point1, interp_point2, interp_point3, interp_connect1, interp_connect2, tertiary_connect, moving_point

# 创建动画
ani = FuncAnimation(fig, animate, init_func=init, frames=len(t_values), interval=50, blit=True)

# 保存动画为 GIF
ani.save('cubic_bezier_animation.gif', writer='pillow', fps=20)

# 关闭图形以防止内存问题
plt.close()